package tests;

import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Random;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.*;

public class GridSquareComponent extends JPanel
{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	//static variables

	private int xsquares = 1;
	private int ysquares = 1;
	private JFrame g;

	private int width;
	private int height;
	private boolean[][] map;
	private Random rnd;
	
	public GridSquareComponent(int xsquares, int ysquares, JFrame g) {
		this.width = xsquares;
		this.height = ysquares;
		this.xsquares = xsquares*2-1;
		this.ysquares = ysquares*2-1;
		this.g = g;
		
		Random a = new Random();
		//maze.generateMultipleChoice(a.nextInt(),10);
		this.generate(a.nextInt());
	}

	public void paintComponent(Graphics g)
	{
	

		
		// fill with the color you want
		g.setColor(Color.GREEN);
		g.fillRect(0, 0, this.g.getWidth(), this.g.getHeight());
		g.setColor(Color.WHITE);
		
		// go into Graphics2D for all the fine art, more options
		// optional, here I just get variable Stroke sizes
		Rectangle2D rect; 
		Graphics2D g2 = (Graphics2D) g;
		g2.setStroke(new BasicStroke(1));

		
		// that will have a little glitch with the integer math
		// the grid will have the bottom row slightly larger
		// to overcome that, you must use double
		
		double rowH = getHeight() / (double)this.ysquares;
		double colW = getWidth() / (double)this.xsquares;
		for (int i = 0; i < this.xsquares; i++) {
			for (int j = 0; j < this.ysquares; j++) {
				if (this.getSquare(i, j)) {
					rect = new Rectangle2D.Double(colW*i, rowH*j, colW, rowH);
					g2.fill(rect);
				}
			}
		}
	}
	
	
	
	
	public void generateMultipleChoice(long seed, int whiteNoise) {
		generate(seed);
		Random b = new Random();
		for (int i = 0; i < this.width*2-1; i++) {
			for (int j = 0; j < this.height*2-1;j++) {
				if ((i+j)%2 != 0)
					if (b.nextInt(whiteNoise) == 0)
						this.map[i][j] = true;
			}
		}
	}
	public void generate(long seed) {
		System.out.println("Generando nuevo laberinto (" + this.width + "," + this.height + ")con semilla: " + seed);
		long time = System.currentTimeMillis();
		
		this.map = new boolean[width*2-1][height*2-1];
		rnd = new Random(seed);
		double prob = rnd.nextDouble();
		
		System.out.println("Probabilidad de parar: " + prob);
		
		int sX = (width/2)*2;
		int sY = (height/2)*2;
		
		this.map[sX][sY] = true;
		generate(sX,sY,prob);
		//this.map[xStart*2][yStart*2] = true;
		//generate(xStart*2,yStart*2);
		System.out.println("Colisiones: " + this.getColisiones());
		System.out.println("Tiempo: " + (System.currentTimeMillis() - time) + "ms");
	}
	private void generate(int x, int y, double probability) {
		ArrayList<Point> points = new ArrayList<Point>();
		Point cPoint;
		int xPos = x;
		int yPos = y;
		
		int dir = rnd.nextInt(4);
		points.add(new Point(x,y, dir));
		
		int numTimesNoReturn = 0;
		
		while(true) {
			this.update(getGraphics());

			cPoint = points.get(points.size()-1);
			xPos = cPoint.getX();
			yPos = cPoint.getY();
			dir = cPoint.getDir();
			
			if (!this.nextPlace(xPos,yPos,dir)) {
				if (numTimesNoReturn * probability >= 1) {
					numTimesNoReturn = 0;
					randomizeArray(points, rnd);
				}
				else {
					this.setPath(xPos,yPos,dir);
					//if (nextX(xPos,dir) == 0 && nextY(yPos,dir) == 0)
						//return;
					points.add(new Point(nextX(xPos,dir),nextY(yPos,dir),rnd.nextInt(4)));
					numTimesNoReturn++;
				}
			}
			else {
				numTimesNoReturn = 0;
				if (cPoint.nextDir() == cPoint.getStartDir()) {
					points.remove(points.size()-1);
				if (points.size()== 0) break; //end while
					//randomizeArray(points, rnd.nextInt(points.size()));
				}

			}
		}
	}
	private void randomizeArray(ArrayList<Point> a, Random rand) {
		int rnd = rand.nextInt(a.size());
		Point tmp = a.get(rnd);
		a.set(rnd, a.get(a.size()-1));
		a.set(a.size()-1,tmp);
	}
	
	private void setPath(int x, int y, int dir) {
		switch(dir) {
		case 0: {
			this.map[x][y-1] = true;
			this.map[x][y-2] = true;
			break;
		}
		case 1:{
			this.map[x+1][y] = true;
			this.map[x+2][y] = true;
			break;
		}
		case 2:{
			this.map[x][y+1] = true;
			this.map[x][y+2] = true;
			break;
		}
		case 3:{
			this.map[x-1][y] = true;
			this.map[x-2][y] = true;
			break;
		}
		}
	}
	private int nextY(int y, int dir) {
		if (dir == 0)
			return y-2;
		if (dir == 2)
			return y+2;
		return y;
	}
	private int nextX(int x, int dir) {
		if (dir == 1)
			return x+2;
		if (dir == 3)
			return x-2;
		return x;
	}
	private boolean nextPlace(int x, int y, int dir) {
		switch(dir) {
		case 0: {
			if (y == 0)
				return true;
			return this.map[x][y-2];
		}
		case 1: {
			if (x >= this.width*2-2)
				return true;
			return this.map[x+2][y];
		}
		case 2: {
			if (y >= this.height*2-2)
				return true;
			return this.map[x][y+2];
		}
		case 3: {
			if (x == 0)
				return true;
			return this.map[x-2][y];
		}
		}
		return true;
	}
	private boolean nextSquare(int x, int y, int dir) {
		switch(dir) {
		case 0: {
			if (y == 0)
				return false;
			return this.map[x][y-1];
		}
		case 1: {
			if (x >= this.width*2-2)
				return false;
			return this.map[x+1][y];
		}
		case 2: {
			if (y >= this.height*2-2)
				return false;
			return this.map[x][y+1];
		}
		case 3: {
			if (x == 0)
				return false;
			return this.map[x-1][y];
		}
		}
		return true;
	}
	public boolean getSquare(int x, int y) {
		return this.map[x][y];
	}
	private int getColisiones() {
		int colisiones = 0;
		for (int i = 0; i < this.width; i++) 
			for (int j = 0; j < this.height; j++) {
				int cont = 0;
				for (int k = 0; k < 4; k++) {
					if (nextSquare(i*2,j*2,k))
						cont++;
				}
				if (cont == 1)
					colisiones++;
			}
		return colisiones;
	}
	
}



